﻿package bdz.extensions.b2d
{
	import Box2D.Dynamics.Joints.b2MouseJoint;
	import Box2D.Dynamics.Joints.b2MouseJointDef;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.utils.*;
	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.*;
	import General.Input;
	import Box2D.Collision.Shapes.b2PolygonDef;
	
	/**
	 * ...
	 * @author ...
	 */
	public class B2Environment extends MovieClip
	{
		//public static const POLYGON:Class = b2PolygonDef;
		//public static const CIRCLE:Class = b2CircleDef;
		
		private var timestep:Number = 1 / 20;
		private var iterations:int = 30;
		
		private var world:b2World;
		private var wAABB:b2AABB;
		
		private var body:b2Body;
		
		private var autoRender:Boolean = true;
		private var bounds:Number = 100;
		private var gx:Number = 0;
		private var gy:Number = 10;
		private var world_scale:Number = 30;
		private var dimension:Point = new Point(680, 420);
		
		private var enableMouse:Boolean = true;
		private var world_input:Input;
		
		private var mouseXPhys:Number;
		private var mouseYPhys:Number;
		private var activeJoint:b2MouseJoint;
		private var actualMouseX:Number;
		private var actualMouseY:Number;
		private var mouseVector:b2Vec2 = new b2Vec2();
		private var floor:b2Body;
		private var floorDef:b2BodyDef;
		
		function B2Environment() {
			
			getQualifiedClassName(new b2PolygonDef());
		}
		private function onEnvMouseUp(evt:MouseEvent):void {
			try {	
				world.DestroyJoint(activeJoint);
				activeJoint = null;
			}catch (e:Error) {
				
			}
		}
		private function onEnvMouseDown(evt:MouseEvent):void {
			if (activeJoint == null) {
				
				var bod:b2Body = getMouseBody();
				
				if (bod) {
					var mdef:b2MouseJointDef = new b2MouseJointDef();
					mdef.body1 = world.GetGroundBody();
					mdef.body2 = bod;
					mdef.target.Set(mouseXPhys, mouseYPhys);
					mdef.maxForce = 300 * bod.GetMass();
					mdef.timeStep = timestep;
					activeJoint = world.CreateJoint(mdef) as b2MouseJoint;
					bod.WakeUp();
				}
				
			}
			
				
		}
		private function handleMousePos():void {
			
			mouseXPhys = Input.mouseX / world_scale;
			mouseYPhys = Input.mouseY / world_scale;
			actualMouseX = Input.mouseX;
			actualMouseY = Input.mouseY;
			
			if (activeJoint) {
				activeJoint.SetTarget(new b2Vec2(mouseXPhys, mouseYPhys));
			}
		}
		
		private function getMouseBody():b2Body {
			
			mouseVector.Set(mouseXPhys, mouseYPhys);
			
			var aabb:b2AABB = new b2AABB();
			aabb.lowerBound.Set(mouseXPhys - 0.001, mouseYPhys - 0.001);
			aabb.upperBound.Set(mouseXPhys + 0.001, mouseYPhys + 0.001);
			var iter:int = 10;
			var shapes:Array = new Array();
			
			var count:int = world.Query(aabb, shapes, iter);
			
			for (var i:int = 0; i < count; i++) {
				if (!shapes[i].GetBody().IsStatic()) {
					var tshape:b2Shape = shapes[i] as b2Shape;
					var hit:Boolean = tshape.TestPoint(tshape.GetBody().GetXForm(), mouseVector);
					if (hit) {
						return tshape.GetBody();
					}
				}
			}
			return null;
			
		}
		private function createFloor():void {
			
		}
		private function createLeftWall():void {
			
		}
		
		public function render(evt:*= null):void {
			if (enableMouse) {
				handleMousePos();
				
			}
			world.Step(timestep, iterations);
			for (var bb:b2Body = world.m_bodyList; bb; bb = bb.m_next) {
				if (bb.m_userData is Sprite) {
					try{
						bb.m_userData.x = bb.GetPosition().x * world_scale;
						bb.m_userData.y = bb.GetPosition().y * world_scale;
						bb.m_userData.rotation = bb.GetAngle() * (180 / Math.PI);
						if (bb.m_userData.y > floor.m_userData.y+100) {	
							removeChild(bb.m_userData);
							bb.m_userData = null;
							world.DestroyBody(bb);
							
						}
						
					}catch (e:Error) {
						
					}
				}
			}
		}
		public function initialize():void {
			wAABB = new b2AABB();
			
			wAABB.lowerBound.Set( -bounds, -bounds);
			wAABB.upperBound.Set(bounds, bounds);
			world = new b2World(wAABB, new b2Vec2(gx, gy), true);
			if (autoRender) {
				addEventListener(Event.ENTER_FRAME, render);
			}
			world_input = new Input(this);
			if (enableMouse) {
				stage.addEventListener(MouseEvent.MOUSE_UP, onEnvMouseUp);
				stage.addEventListener(MouseEvent.MOUSE_DOWN, onEnvMouseDown);
			}
		}
		public function modifyFloor(w:Number, h:Number, x:Number, y:Number):void {
			try {
				world.DestroyBody(floor);
				floor = null;
			}catch (e:Error) {
				
			}
			floorDef.position.x = x / world_scale || (dimension.x/2)/world_scale;
			floorDef.position.y = y / world_scale || dimension.y / world_scale;
			
			var b2body:b2PolygonDef = new b2PolygonDef();
			b2body.SetAsBox((w/world_scale/2), (h/world_scale/2));
			b2body.density = 0;
			b2body.friction = .3;
			floorDef.userData.width = w
			floorDef.userData.height = h
			
			floor = world.CreateBody(floorDef);
			
			floor.CreateShape(b2body);
			floor.SetMassFromShapes();
		}
		public function addFloor(sprite:Object, w:Number, h:Number):void {
			
			
			if (!floor) {
				floorDef = new b2BodyDef();
				floorDef.position.x = (dimension.x/2)/world_scale;
				floorDef.position.y = dimension.y / world_scale;
				
				
				var b2body:b2PolygonDef = new b2PolygonDef();
				b2body.SetAsBox((w/world_scale/2), (h/world_scale/2));
				b2body.density = 0;
				b2body.friction = .3;
				
				floorDef.userData = new sprite();
				
				floorDef.userData.width = w
				floorDef.userData.height = h
				
				addChild(floorDef.userData);
				floor = world.CreateBody(floorDef);
				
				floor.CreateShape(b2body);
				floor.SetMassFromShapes();
			}
			
			
		}
		public function addLeftWall(sprite:Object):void {
			
		}
		public function addRightWall(sprite:Object):void {
			
		}
		public function addSimpleBody(sprite:Object, b2bod:*, props:Object):b2Body {
			var def:b2BodyDef = new b2BodyDef();
			def.isBullet = true;
			try {
				def.position.x = props.x/world_scale;
				def.position.y = props.y/world_scale;
			}catch (e:Error) {
				
			}
			
			var b2body:* = new b2bod();
			b2body.density = props.density || 1;
			b2body.friction = props.friction || .5;
			b2body.restitution = props.restitution || .2;
			if (b2body is b2PolygonDef) {
				b2body.SetAsBox(props.width/world_scale/2 || 1, props.height/world_scale/2 || 1);
			}else {
				b2body.radius = props.radius/world_scale/2 || 1;
			}
			def.userData = sprite;
			def.userData.width = props.width || props.radius || 60;
			def.userData.height =  props.height || props.radius || 60;
			body = world.CreateBody(def);
			body.CreateShape(b2body);
			body.SetMassFromShapes();
			
			
			addChild(def.userData);
			
			return body;
		}
		public function destroyBody(b:b2Body):void {
			removeChild(b.m_userData);
			b.m_userData = null;
			world.DestroyBody(b);
		}
		public function addPully(...args):void {
			
		}
		public function setStageDimension(w:Number, h:Number):void {
			dimension = new Point(w, h);
		}
		
		
	}
	
}